﻿/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkExtractGeometry.cxx

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/

#include "FITKExtractGeometry.h"

#include "vtkCell.h"
#include "vtkCellData.h"
#include "vtkCellIterator.h"
#include "vtkFloatArray.h"
#include "vtkIdList.h"
#include "vtkImplicitFunction.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkObjectFactory.h"
#include "vtkPointData.h"
#include "vtkSmartPointer.h"
#include "vtkUnstructuredGrid.h"

vtkStandardNewMacro(FITKExtractGeometry);
vtkCxxSetObjectMacro(FITKExtractGeometry, ImplicitFunction, vtkImplicitFunction);

//----------------------------------------------------------------------------
// Construct object with ExtractInside turned on.
FITKExtractGeometry::FITKExtractGeometry(vtkImplicitFunction* f)
{
    this->ImplicitFunction = f;
    if (this->ImplicitFunction)
    {
        this->ImplicitFunction->Register(this);
    }

    this->ExtractInside = 1;
    this->ExtractBoundaryCells = 0;
    this->ExtractOnlyBoundaryCells = 0;
}

//----------------------------------------------------------------------------
FITKExtractGeometry::~FITKExtractGeometry()
{
    this->SetImplicitFunction(nullptr);
}

// Overload standard modified time function. If implicit function is modified,
// then this object is modified as well.
vtkMTimeType FITKExtractGeometry::GetMTime()
{
    vtkMTimeType mTime = this->MTime.GetMTime();
    vtkMTimeType impFuncMTime;

    if (this->ImplicitFunction != nullptr)
    {
        impFuncMTime = this->ImplicitFunction->GetMTime();
        mTime = (impFuncMTime > mTime ? impFuncMTime : mTime);
    }

    return mTime;
}


QList< int > FITKExtractGeometry::getSelectOriginalPoints() const
{
    return _originalCurrentPointMap.keys();
}


QList< int > FITKExtractGeometry::getSelectOriginalCells() const
{
    return _originalCurrentCellMap.keys();
}

const QHash< int, int >& FITKExtractGeometry::getPointMap() const
{
    return _originalCurrentPointMap;
}

const QHash< int, int >& FITKExtractGeometry::getCellMap() const
{
    return _originalCurrentCellMap;
}

//----------------------------------------------------------------------------
int FITKExtractGeometry::RequestData(
    vtkInformation*        vtkNotUsed(request),
    vtkInformationVector** inputVector,
    vtkInformationVector*  outputVector)
{
    // Clear cache data.
    _originalCurrentPointMap.clear();
    _originalCurrentCellMap.clear();

    // get the info objects
    vtkInformation* inInfo = inputVector[0]->GetInformationObject(0);
    vtkInformation* outInfo = outputVector->GetInformationObject(0);

    // get the input and output
    vtkDataSet* input = vtkDataSet::SafeDownCast(
        inInfo->Get(vtkDataObject::DATA_OBJECT()));
    vtkUnstructuredGrid* output = vtkUnstructuredGrid::SafeDownCast(
        outInfo->Get(vtkDataObject::DATA_OBJECT()));

    // May be nullptr, check before dereferencing.
    vtkUnstructuredGrid* gridInput = vtkUnstructuredGrid::SafeDownCast(input);

    vtkIdType                          ptId, numPts, numCells, i, newCellId, newId, *pointMap;
    vtkSmartPointer< vtkCellIterator > cellIter =
        vtkSmartPointer< vtkCellIterator >::Take(input->NewCellIterator());
    vtkIdList*    pointIdList;
    int           cellType;
    vtkIdType     numCellPts;
    double        x[3];
    double        multiplier;
    vtkPoints*    newPts;
    vtkIdList*    newCellPts;
    vtkPointData* pd = input->GetPointData();
    vtkCellData*  cd = input->GetCellData();
    vtkPointData* outputPD = output->GetPointData();
    vtkCellData*  outputCD = output->GetCellData();
    int           npts;

    vtkDebugMacro(<< "Extracting geometry");

    if (!this->ImplicitFunction)
    {
        vtkErrorMacro(<< "No implicit function specified");
        return 1;
    }

    // As this filter is doing a subsetting operation, set the Copy Tuple flag
    // for GlobalIds array so that, if present, it will be copied to the output.
    outputPD->CopyGlobalIdsOn();
    outputCD->CopyGlobalIdsOn();

    newCellPts = vtkIdList::New();
    newCellPts->Allocate(VTK_CELL_SIZE);

    if (this->ExtractInside)
    {
        multiplier = 1.0;
    }
    else
    {
        multiplier = -1.0;
    }

    // Loop over all points determining whether they are inside the
    // implicit function. Copy the points and point data if they are.
    //
    numPts = input->GetNumberOfPoints();
    numCells = input->GetNumberOfCells();
    pointMap = new vtkIdType[numPts];  // maps old point ids into new
    for (i = 0; i < numPts; i++)
    {
        pointMap[i] = -1;
    }

    output->Allocate(numCells / 4);  //allocate storage for geometry/topology
    newPts = vtkPoints::New();
    newPts->Allocate(numPts / 4, numPts);
    outputPD->CopyAllocate(pd);
    outputCD->CopyAllocate(cd);
    vtkFloatArray* newScalars = nullptr;

    if (!this->ExtractBoundaryCells)
    {
        for (ptId = 0; ptId < numPts; ptId++)
        {
            input->GetPoint(ptId, x);
            if ((this->ImplicitFunction->FunctionValue(x) * multiplier) < 0.0)
            {
                newId = newPts->InsertNextPoint(x);
                pointMap[ptId] = newId;
                _originalCurrentPointMap.insert(ptId, newId);
                outputPD->CopyData(pd, ptId, newId);
            }
        }
    }
    else
    {
        // To extract boundary cells, we have to create supplemental information
        double val;
        newScalars = vtkFloatArray::New();
        newScalars->SetNumberOfValues(numPts);

        for (ptId = 0; ptId < numPts; ptId++)
        {
            input->GetPoint(ptId, x);
            val = this->ImplicitFunction->FunctionValue(x) * multiplier;
            newScalars->SetValue(ptId, val);
        }
    }

    // Now loop over all cells to see whether they are inside implicit
    // function (or on boundary if ExtractBoundaryCells is on).
    //
    for (cellIter->InitTraversal(); !cellIter->IsDoneWithTraversal();
        cellIter->GoToNextCell())
    {
        cellType = cellIter->GetCellType();
        numCellPts = cellIter->GetNumberOfPoints();
        pointIdList = cellIter->GetPointIds();

        newCellPts->Reset();
        if (!this->ExtractBoundaryCells)  //requires less work
        {
            for (npts = 0, i = 0; i < numCellPts; i++, npts++)
            {
                ptId = pointIdList->GetId(i);
                if (pointMap[ptId] < 0)
                {
                    break;  //this cell won't be inserted
                }
                else
                {
                    newCellPts->InsertId(i, pointMap[ptId]);
                }
            }
        }  //if don't want to extract boundary cells

        else  //want boundary cells
        {
            for (npts = 0, i = 0; i < numCellPts; i++)
            {
                ptId = pointIdList->GetId(i);
                if (newScalars->GetValue(ptId) <= 0.0)
                {
                    npts++;
                }
            }
            int extraction_condition = 0;
            if (this->ExtractOnlyBoundaryCells)
            {
                if ((npts > 0) && (npts != numCellPts))
                {
                    extraction_condition = 1;
                }
            }
            else
            {
                if (npts > 0)
                {
                    extraction_condition = 1;
                }
            }
            if (extraction_condition)
            {
                for (i = 0; i < numCellPts; i++)
                {
                    ptId = pointIdList->GetId(i);
                    if (pointMap[ptId] < 0)
                    {
                        input->GetPoint(ptId, x);
                        newId = newPts->InsertNextPoint(x);
                        pointMap[ptId] = newId;
                        _originalCurrentPointMap.insert(ptId, newId);
                        outputPD->CopyData(pd, ptId, newId);
                    }
                    newCellPts->InsertId(i, pointMap[ptId]);
                }
            }  //a boundary or interior cell
        }      //if mapping boundary cells

        int extraction_condition = 0;
        if (this->ExtractOnlyBoundaryCells)
        {
            if (npts != numCellPts && (this->ExtractBoundaryCells && npts > 0))
            {
                extraction_condition = 1;
            }
        }
        else
        {
            if (npts >= numCellPts || (this->ExtractBoundaryCells && npts > 0))
            {
                extraction_condition = 1;
            }
        }
        if (extraction_condition)
        {
            // special handling for polyhedron cells
            if (gridInput && cellType == VTK_POLYHEDRON)
            {
                newCellPts->Reset();
                gridInput->GetFaceStream(cellIter->GetCellId(), newCellPts);
                vtkUnstructuredGrid::ConvertFaceStreamPointIds(newCellPts, pointMap);
            }
            newCellId = output->InsertNextCell(cellType, newCellPts);
            int orignalCellID = cellIter->GetCellId();
            outputCD->CopyData(cd, orignalCellID, newCellId);
            _originalCurrentCellMap.insert(orignalCellID, newCellId);
        }
    }  //for all cells

    // Update ourselves and release memory
    //
    delete[] pointMap;
    newCellPts->Delete();
    output->SetPoints(newPts);
    newPts->Delete();

    if (this->ExtractBoundaryCells)
    {
        newScalars->Delete();
    }

    output->Squeeze();

    return 1;
}

//----------------------------------------------------------------------------
int FITKExtractGeometry::FillInputPortInformation(int, vtkInformation* info)
{
    info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkDataSet");
    return 1;
}

//----------------------------------------------------------------------------
void FITKExtractGeometry::PrintSelf(ostream& os, vtkIndent indent)
{
    this->Superclass::PrintSelf(os, indent);

    os << indent << "Implicit Function: "
        << static_cast<void*>(this->ImplicitFunction) << "\n";
    os << indent << "Extract Inside: "
        << (this->ExtractInside ? "On\n" : "Off\n");
    os << indent << "Extract Boundary Cells: "
        << (this->ExtractBoundaryCells ? "On\n" : "Off\n");
    os << indent << "Extract Only Boundary Cells: "
        << (this->ExtractOnlyBoundaryCells ? "On\n" : "Off\n");
}
